#include "pch.hpp"
#include "D3D11EffectPass.hpp"

#include "D3D11RasterizerState.hpp"
#include "D3D11BlendState.hpp"
#include "D3D11Mesh.hpp"
#include "D3D11ConstantsBuffer.hpp"

using namespace Engine;
using namespace Engine::Video;




D3D11EffectPass::D3D11EffectPass(const ID3D11DevicePtr& device)
	: m_device(device)
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




void D3D11EffectPass::addVertexShader(const QByteArray& asciiData, const std::string& entryPoint, const StaticReflection& vertexReflection)
{
	if(m_vertexShader)
		BOOSTEXT_THROW(exception("This pass already has a vertex shader"));

	m_vertexShader = createShader<Vertex>(m_device, compileShader<Vertex>(asciiData, entryPoint));
	m_vertexReflection = vertexReflection;
	createInputLayout();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11EffectPass::addPixelShader(const QByteArray& asciiData, const std::string& entryPoint)
{
	if(m_pixelShader)
		BOOSTEXT_THROW(exception("This pass already has a pixel shader"));

	// There must be a vertex shader, if there's not then
	// complain about it
	if(!m_vertexShader)
		BOOSTEXT_THROW(exception("Adding a pixel shader requires a vertex shader to be present first"));

	m_pixelShader = createShader<Pixel>(m_device, compileShader<Pixel>(asciiData, entryPoint));
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void D3D11EffectPass::optimize()
{
	m_vertexShader->optimize();
	m_pixelShader->optimize();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11EffectPass::draw(const ID3D11DevicePtr& device, D3D11DeviceContext& context, const RenderPass& pass)
{
	if(!m_vertexShader)
		BOOSTEXT_THROW(exception("The pass cannot be drawn: There is no vertex shader"));
	if(!m_pixelShader)
		BOOSTEXT_THROW(exception("The pass cannot be drawn: There is no pixel shader"));


	D3D11Mesh::ptr mesh = boost::shared_polymorphic_cast<D3D11Mesh>(pass.mesh);

	if(*m_vertexReflection != mesh->reflect())
		BOOSTEXT_THROW(exception("The mesh's vertex layout doesn't match this passes' vertex shader's"));

	// Set the input layout for this mesh and pass it to the device
	context.IASetInputLayout(m_inputLayout);
	mesh->stream(context);

	m_vertexShader->apply(device, context, pass.vertexShader);
	m_pixelShader->apply(device, context, pass.pixelShader);

	// Set rasterizer and blend states
	if(pass.rasterizerState)
	{
		D3D11RasterizerState::ptr state = boost::shared_polymorphic_cast<D3D11RasterizerState>(pass.rasterizerState);
		const ID3D11RasterizerStatePtr& handle = state->apply(device, context);
		context.RSSetState(handle ? handle.get() : NULL);
	}
	else
	{
		context.RSSetState(NULL);
	}

	if(pass.blendState)
	{
		D3D11BlendState::ptr state = boost::shared_polymorphic_cast<D3D11BlendState>(pass.blendState);
		const ID3D11BlendStatePtr& handle = state->apply(device, context);
		context.OMSetBlendState(handle ? handle.get() : NULL);
	}
	else
	{
		context.OMSetBlendState(NULL);
	}

	// Draw the sucker
	mesh->draw(context);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11EffectPass::createInputLayout()
{
	if(!m_vertexShader)
		BOOSTEXT_THROW(exception("Can't create input layout for the vertex shader: There is none present"));

	D3D11_INPUT_ELEMENT_DESC_ARRAY desc = D3D11Mesh::inputDescription(*m_vertexReflection);
	ID3D10BlobPtr compiledShader = m_vertexShader->compiled();
	ID3D11InputLayout* layout = NULL;
	D3D11_API(m_device->CreateInputLayout(desc.get(),
		boost::numeric_cast<uint32>(m_vertexReflection->elements()),
		compiledShader->GetBufferPointer(),
		boost::numeric_cast<uint32>(compiledShader->GetBufferSize()),
		&layout));

	m_inputLayout = ID3D11InputLayoutPtr(layout);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
